package com.hongguowei.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

//package com.hongguowei.config;
//
//
//import com.hongguowei.entity.Author;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Scope;
//
//@Configuration
//public class TestConfiguration {
//
//    public TestConfiguration() {
//        System.out.println("TestConfiguration 容器启动初始化...");
//    }
//
//    //@Bean注解注册bean,同时可以指定初始化和销毁方法
//    @Bean(name = "testBean")
//    @Scope("prototype")
//    public Author user() {
//        return new Author();
//    }
//}

/**
 * 事务
 *
 * @author qiantuo
 */
//声明试事务
@Transactional(rollbackFor = Throwable.class, timeout = 1000, readOnly = true)
public class TestConfiguration {
    //编程试事务
    @Autowired
    private TransactionTemplate transactionTemplate;

    /**
     * 事务的传播行为:
     * 当事务方法被另一个事务方法调用时，必须指定事务应该如何传播。
     * 例如：方法可能继续在现有事务中运行，也可能开启一个新事务，并在自己的事务中运行。
     * Spring定义了七种传播行为
     */
    public void Test() {
        //如果当前没有事务，就新建一个事务，如果已经存在一个事务，则加入到这个事务中。这是最常见的选择。
        int propagationRequired = TransactionDefinition.PROPAGATION_REQUIRED;
        //	支持当前事务，如果当前没有事务，就以非事务方式执行。
        int propagationSupports = TransactionDefinition.PROPAGATION_SUPPORTS;
        //表示该方法必须在事务中运行，如果当前事务不存在，则会抛出一个异常
        int propagationMandatory = TransactionDefinition.PROPAGATION_MANDATORY;
        //表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务，在该方法执行期间，当前事务会被挂起。
        int propagationRequiresNew = TransactionDefinition.PROPAGATION_REQUIRES_NEW;
        //表示该方法不应该运行在事务中。如果当前存在事务，就把当前事务挂起。
        int propagationNotSupported = TransactionDefinition.PROPAGATION_NOT_SUPPORTED;
        //表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行，则会抛出异常
        int propagationNever = TransactionDefinition.PROPAGATION_NEVER;
        //	如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则执行与PROPAGATION_REQUIRED类似的操作。
        int propagationNested = TransactionDefinition.PROPAGATION_NESTED;
    }

    /**
     * 隔离规则：
     * 脏读（Dirty reads）—— 事务A读取了事务B更新的数据，然后B回滚操作，那么A读取到的数据是脏数据。
     * 不可重复读（Nonrepeatable read）—— 事务 A 多次读取同一数据，事务 B 在事务A多次读取的过程中，对数据作了更新并提交，导致事务A多次读取同一数据时，结果不一致。
     * 幻读（Phantom read）—— 系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级，但是系统管理员B就在这个时候插入了一条具体分数的记录，当系统管理员A改结束后发现还有一条记录没有改过来，就好像发生了幻觉一样，这就叫幻读。
     * （不可重复读的和幻读很容易混淆，不可重复读侧重于修改，幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行，解决幻读需要锁表）
     * Spring中定义了五种隔离规则:
     */
    public void Test1() {
        //使用后端数据库默认的隔离级别
        int isolationDefault = TransactionDefinition.ISOLATION_DEFAULT;
        //允许读取尚未提交的数据变更(最低的隔离级别)-- 脏读，不可重复读，幻读
        int isolationReadUncommitted = TransactionDefinition.ISOLATION_READ_UNCOMMITTED;
        //允许读取并发事务已经提交的数据 --  不可重复读，幻读
        int isolationReadCommitted = TransactionDefinition.ISOLATION_READ_COMMITTED;
        //	对同一字段的多次读取结果都是一致的，除非数据是被本身事务自己所修改 -- 幻读
        int isolationRepeatableRead = TransactionDefinition.ISOLATION_REPEATABLE_READ;
        //最高的隔离级别，完全服从ACID的隔离级别，也是最慢的事务隔离级别，因为它通常是通过完全锁定事务相关的数据库表来实现的
        //ISOLATION_SERIALIZABLE 隔离规则类型在开发中很少用到。举个很简单的例子。咱们使用了ISOLATION_SERIALIZABLE规则。A,B两个事务操作同一个数据表并发过来了。A先执行。A事务这个时候会把表给锁住，B事务执行的时候直接报错。
        int isolationSerializable = TransactionDefinition.ISOLATION_SERIALIZABLE;
    }

    /**
     * 结果：两个函数操作的数据都会回滚
     */
    public class A {

        @Transactional(rollbackFor = Exception.class)
        public void aFunction() {
            //todo: 数据库操作A(增，删，该)
            //调用内部没有添加@Transactional注解的函数
            aInnerFunction();
        }

        private void aInnerFunction() {
            //todo: 操作数据B(做了增，删，改 操作)
            throw new RuntimeException("函数执行有异常!");
        }

    }

    /**
     *  结果：同第一种情况一样，两个函数对数据库操作都会回滚。因为同一个类中函数相互调用的时候，
     *  内部函数添加@Transactional注解无效。@Transactional注解只有外部调用才有效。
     */
    public class B {

        @Transactional(rollbackFor = Exception.class)
        public void aFunction() {
            //todo: 数据库操作A(增，删，该)
            aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
        }
        @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
        public void aInnerFunction() {
            //todo: 操作数据B(做了增，删，改 操作)
            throw new RuntimeException("函数执行有异常!");
        }

    }

    /**
     * 结果：两个函数对数据库的操作都不会回滚。因为内部函数@Transactional注解添加和没添加一样。
     */
    public class C {

        public void aFunction() {
            //todo: 数据库操作A(增，删，该)
            aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
        }

        @Transactional(rollbackFor = Exception.class)
        protected void aInnerFunction() {
            //todo: 操作数据B(做了增，删，改 操作)
            throw new RuntimeException("函数执行有异常!");
        }

    }

    /**
     * 结果：两个函数里面的数据库操作都成功。事务回滚的动作发生在当有@Transactional注解函数有对应异常抛出时才会回滚
     */
    public class D {

        @Transactional(rollbackFor = Exception.class)
        public void aFunction() {
            //todo: 数据库操作A(增，删，该)
            try {
                aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void aInnerFunction() {
            //todo: 操作数据B(做了增，删，改 操作)
            throw new RuntimeException("函数执行有异常!");
        }

    }
}
